#ifndef H_Reusable
#define H_Reusable

#include "ResourceDefs.hh"
#include "Resource.hh"
#include <map>
#include <vector>

namespace EUROPA {
    class Reusable : public Resource {
    public:
      //initial capacity is the upper limit, maxInstConsumption == maxInstProduction, maxConsumption == maxProduction
      Reusable(const PlanDatabaseId planDatabase, const std::string& type, const std::string& name, const std::string& detectorName, const std::string& profileName,
	       edouble initCapacityLb = 0, edouble initCapacityUb = 0, edouble lowerLimit = MINUS_INFINITY,
	       edouble maxInstConsumption = PLUS_INFINITY,
	       edouble maxConsumption = PLUS_INFINITY);
      Reusable(const PlanDatabaseId planDatabase, const std::string& type, const std::string& name, bool open);
      Reusable(const ObjectId parent, const std::string& type, const std::string& localName, bool open);
      virtual ~Reusable();

      virtual void getOrderingChoices(const TokenId token,
				      std::vector<std::pair<TokenId, TokenId> >& results,
				      unsigned long limit = std::numeric_limits<unsigned long>::max());
      void getOrderingChoices(const InstantId token,
			      std::vector<std::pair<TransactionId, TransactionId> >& results,
			      unsigned long limit = std::numeric_limits<unsigned long>::max()) {Resource::getOrderingChoices(token, results, limit);}
      PSList<PSEntityKey> getOrderingChoices(TimePoint t) {return Resource::getOrderingChoices(t);}
    protected:
      void createTransactions(const TokenId tok);
      void removeTransactions(const TokenId tok);
      void addToProfile(const TokenId tok);
      void removeFromProfile(const TokenId tok);

      std::map<TokenId, std::pair<TransactionId, TransactionId> > m_tokensToTransactions;
    };

    typedef Id<Reusable> ReusableId;

    class UnaryTimeline : public Reusable {
    public:
      UnaryTimeline(const PlanDatabaseId planDatabase, const std::string& type, const std::string& name, bool open = false);
      UnaryTimeline(const ObjectId parent, const std::string& type, const std::string& name, bool open = false);
      virtual ~UnaryTimeline();
    };

    class CBReusable;
    class Uses;

    typedef Id<CBReusable> CBReusableId;
    typedef Id<Uses> UsesId;

    class CBReusable : public Resource {
    public:
      //initial capacity is the upper limit, maxInstConsumption == maxInstProduction, maxConsumption == maxProduction
      CBReusable(const PlanDatabaseId planDatabase, const std::string& type, const std::string& name, const std::string& detectorName, const std::string& profileName,
           edouble initCapacityLb = 0, edouble initCapacityUb = 0, edouble lowerLimit = MINUS_INFINITY,
           edouble maxInstConsumption = PLUS_INFINITY,
           edouble maxConsumption = PLUS_INFINITY);
      CBReusable(const PlanDatabaseId planDatabase, const std::string& type, const std::string& name, bool open);
      CBReusable(const ObjectId parent, const std::string& type, const std::string& localName, bool open);
      virtual ~CBReusable();

      virtual void notifyViolated(const InstantId inst, Resource::ProblemType problem);
      virtual void notifyNoLongerViolated(const InstantId inst);
      virtual void notifyFlawed(const InstantId inst);
      virtual void notifyNoLongerFlawed(const InstantId inst);

    protected:
      void addToProfile(const ConstraintId c);
      void removeFromProfile(const ConstraintId c);
      std::set<UsesId> getConstraintsForInstant(const InstantId instant);

      void addToProfile(TransactionId t);
      void removeFromProfile(TransactionId t);

      // TODO: only needed for backwards compatibility with Resource API, rework hierarchy to fix this.
      void addToProfile(const TokenId tok);
      void removeFromProfile(const TokenId tok);
      void createTransactions(const TokenId) {}
      void removeTransactions(const TokenId) {}

      std::map<UsesId, std::pair<TransactionId, TransactionId> > m_constraintsToTransactions;

      friend class Uses;
    };

    class Uses : public Constraint
    {
    public:
      Uses(const std::string& name,
           const std::string& propagatorName,
           const ConstraintEngineId constraintEngine,
           const std::vector<ConstrainedVariableId>& scope);

      static const std::string& CONSTRAINT_NAME();
      static const std::string& PROPAGATOR_NAME();

      static const int RESOURCE_VAR = 0;
      static const int QTY_VAR = 1;
      static const int START_VAR = 2;
      static const int END_VAR = 3;

      virtual std::string getViolationExpl() const;

      const TransactionId getTransaction(int var) const;

      CBReusableId getResource() const;

      const std::map<InstantId,Resource::ProblemType>& getViolationProblems() const;

    protected:
      virtual void handleDiscard();

      virtual void notifyViolated();
      virtual void notifyNoLongerViolated();

      virtual void notifyViolated(Resource::ProblemType problem, const InstantId inst);
      virtual void notifyNoLongerViolated(const InstantId inst);

      CBReusableId m_resource;
      std::vector<TransactionId> m_txns;
      std::map<InstantId,Resource::ProblemType> m_violationProblems; // instant->problem map

    private:
      virtual bool canIgnore(const ConstrainedVariableId variable,
                             unsigned int argIndex,
                             const DomainListener::ChangeType& changeType);

      virtual void handleExecute();
      void handleExecute(const ConstrainedVariableId variable,
			 unsigned int argIndex,
			 const DomainListener::ChangeType& changeType) {Constraint::handleExecute(variable, argIndex, changeType);}

      friend class CBReusable;
    };

}

#endif
